/*
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
 *
 * This file is part of retabel.
 *
 * Copyright (c) 2011 by individual contributors as indicated by the
 * @author tags. See the copyright.txt file in the distribution
 * for a full listing of individual contributors.
 *
 * This is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as
 * published by the Free Software Foundation; either version 2.1 of
 * the License, or (at your option) any later version.
 *
 * This software is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this software; if not, write to the Free
 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
 * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
 */

package org.retabel.backendbean.lazylist;

import org.apache.commons.collections.list.AbstractLinkedList;

import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;

/**
 * This lazy list will load its content partially (in blocks of elements) dynamically.
 * The list is created over a factory and can only be used read only.
 * Some reading methods (indexOf, lastIndexOf, contains, containsAll, toArray) are not supported.
 * The list is only accessed using the list interface,
 * so certain non list interface methods are not explicitely handled.
 *
 * @author Marc Neumann
 */
class OwLazyList extends AbstractLinkedList {

  /**
   * For iterating over the whole lazy list.
   */
  private class OwLazyListIterator extends AbstractLinkedList.LinkedListIterator {

    public OwLazyListIterator(int pFromIndex) {
      super(OwLazyList.this, pFromIndex);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public boolean hasNext() {
      return (nextIndex() < numTotal);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public Object next() {
      return get(nextIndex++);
    }
  }

  /**
   * For iterating over a sub list.
   */
  private class OwLazySubListIterator extends AbstractLinkedList.LinkedSubListIterator {

    public OwLazySubListIterator(AbstractLinkedList.LinkedSubList pSub, int pFromIndex) {
      super(pSub, pFromIndex);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public Object next() {
      return get(nextIndex++);
    }

  }

  private int numTotal;

  private int blockSize;

  private IOwLazyListLoader loader;

  private List[] cachedLists;

  /**
   * Construcor.
   * @param pLoader Used to obtain list elements lazily.
   * @param pNumTotal The total number of elements in the list.
   * @param pBlockSize The elements for dynamic retrieval are queried in blocks of this size.
   */
  public OwLazyList(IOwLazyListLoader pLoader, int pNumTotal, int pBlockSize) {
    numTotal = pNumTotal;
    blockSize = pBlockSize;
    loader = pLoader;

    int numCachedLists = (int) Math.ceil(((float) pNumTotal / (float) pBlockSize));
    cachedLists = new List[numCachedLists];
  }

  /**
   * {@inheritDoc}
   */
  public int size() {
    return numTotal;
  }

  /**
   * {@inheritDoc}
   */
  public Object get(int pIndex) {
    int indexCachedList = pIndex / blockSize;
    int indexInCachedList = pIndex % blockSize;
    int indexBlockStart = pIndex - indexInCachedList;

    List partialList = cachedLists[indexCachedList];
    if (partialList == null) {
      partialList = loader.load(indexBlockStart, blockSize);
      cachedLists[indexCachedList] = partialList;
    }

    Object resultItem;
    if (indexInCachedList < partialList.size()) {
      resultItem = partialList.get(indexInCachedList);
    }
    else {
      resultItem = null;
    }

    return resultItem;
  }

  /**
   * {@inheritDoc}
   */
  public Iterator iterator() {
    return listIterator();
  }

  /**
   * {@inheritDoc}
   */
  public ListIterator listIterator() {
    return listIterator(0);
  }

  /**
   * {@inheritDoc}
   */
  public ListIterator listIterator(int pFromIndex) {
    return new OwLazyListIterator(pFromIndex);
  }

  /**
   * {@inheritDoc}
   */
  protected Iterator createSubListIterator(LinkedSubList pSubList) {
    return new OwLazySubListIterator(pSubList, 0);
  }

  /**
   * {@inheritDoc}
   */
  protected ListIterator createSubListListIterator(LinkedSubList pSubList, int pFromIndex) {
    return new OwLazySubListIterator(pSubList, pFromIndex);
  }

  /**
   * {@inheritDoc}
   */
  public boolean contains(Object pValue) {
    throw new UnsupportedOperationException();
  }

  /**
   * {@inheritDoc}
   */
  public boolean containsAll(Collection pColl) {
    throw new UnsupportedOperationException();
  }

  /**
   * {@inheritDoc}
   */
  public int indexOf(Object pValue) {
    throw new UnsupportedOperationException();
  }

  /**
   * {@inheritDoc}
   */
  public int lastIndexOf(Object pValue) {
    throw new UnsupportedOperationException();
  }

  /**
   * {@inheritDoc}
   */
  public Object[] toArray() {
    throw new UnsupportedOperationException();
  }

  /**
   * {@inheritDoc}
   */
  public Object[] toArray(Object[] pArray) {
    throw new UnsupportedOperationException();
  }

  /**
   * {@inheritDoc}
   */
  public boolean equals(Object pObj) {
    return (this == pObj);
  }

  /**
   * {@inheritDoc}
   */
  public int hashCode() {
    return System.identityHashCode(this);
  }

}